/*! ****************************************************************************
 *
 * \file mainFunc.cc
 * \brief Main function.
 *
 ******************************************************************************/

//==============================================================================
//
//    I N C L U D E S
//
//==============================================================================

#include <boost/asio/io_service.hpp>
#include <boost/asio/signal_set.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <syslog.h>
#include <unistd.h>
#include "ThreadPool.h"
#include "CommandLineParser.h"
//#include "ConfigFileParser.h"

using namespace std;
using namespace tsc::common;
using namespace boost::asio;

namespace tsc
{
namespace common
{
//==============================================================================
//
//    C O N S T A N T S
//
//==============================================================================
const char* gDefaultDaemonName = "DAEMON";
const char* gDefaultConfigFile = "daemon.conf";
const char* gDefaultLogFile    = "/tmp/daemon.log";

//==============================================================================
//
//    F O R W A R D   D E C L A R A T I O N S
//
//==============================================================================

/*! ****************************************************************************
 *
 * \brief
 *
 ******************************************************************************/
extern void parseCommandLine(CommandLineParser* pParser);

/*! ****************************************************************************
 *
 * \brief Initiate sub process with io_services.
 *
 ******************************************************************************/
extern void initSubProcesses(CommandLineParser* pParser/*, ConfigFileParser* pParser*/ );

/*! ****************************************************************************
 *
 * \brief Fork the process
 *
 ******************************************************************************/
int forkMainProcess();

/*! ****************************************************************************
 *
 * \brief Redirect I/O streams to logfile
 *
 ******************************************************************************/
int redirectIoStreams(const char* logFile);

} // namespace common
} // namespace tsc

//==============================================================================
//
//    F U N C T I O N S
//
//==============================================================================

/*! ****************************************************************************
 *
 * \brief Main daemon function
 *
 ******************************************************************************/
int main(int argc, const char* argv[])
{

  CommandLineParser* pCLParser = 0;
//  ConfigFileParser*  pCFParser = 0;

  // get this from command line or config file
  string daemonName = gDefaultDaemonName;
  //string configFile = gDefaultConfigFile;
  string logFile    = gDefaultLogFile;

  try
  {
    pCLParser = new CommandLineParser(argc, argv);
    parseCommandLine(pCLParser);
  }
  catch (ios_base::failure& exc)
  {
    cerr << "Caught exception:\n" << exc.what() << endl;
    delete pCLParser;
    return 1;
  }
/*
  try
  {
    pCFParser = new ConfigFileParser(configFile);
    parseConfigFile(pCFParser);
  }
  catch (std::ios_base::failure& exc)
  {
    cerr << "Caught exception:\n" << exc.what() << endl;
    delete pCLParser;
    delete pCFParser;
    return 1;
  }
*/
  try
  {
    initSubProcesses(pCLParser/*, pCFParser*/);

    ThreadPool::instance()->addSignal(SIGHUP);  // 1
    ThreadPool::instance()->addSignal(SIGINT);  // 2
    ThreadPool::instance()->addSignal(SIGTERM); // 15

    ThreadPool::instance()->notifyFork(io_service::fork_prepare);

    if (forkMainProcess())
    {
      syslog(LOG_INFO | LOG_USER, "Forking of the process failed.");
      return 1;
    }

    if (redirectIoStreams(logFile.data()))
    {
      syslog(LOG_INFO | LOG_USER, "Redirection of I/O streams failed.");
      return 1;
    }

    ThreadPool::instance()->notifyFork(io_service::fork_child);

    syslog(LOG_INFO | LOG_USER, "Daemon %s started.", daemonName.data());
    ThreadPool::instance()->startAll();
    syslog(LOG_INFO | LOG_USER, "Daemon %s stopped.", daemonName.data());
  }
  catch (exception& e)
  {
    syslog(LOG_ERR | LOG_USER, "Caught exception: %s", e.what());
    cerr << "Exception: " << e.what() << endl;
  }

  delete pCLParser;
  //delete pCFParser;
} // main

namespace tsc
{
namespace common
{

//==============================================================================
//
//
//
//==============================================================================
int forkMainProcess()
{
  pid_t pid;

  // Fork the process and have the parent exit.
  pid = fork();
  if (pid)
  {
    if (pid > 0)
    {
      exit(0);
    }
    else
    {
      syslog(LOG_ERR | LOG_USER, "First fork failed: %m");
      return 1;
    }
  }

  // Make the process a new session leader. This detaches it from the
  // terminal.
  setsid();

  chdir("/");
  umask(0);

  // A second fork ensures the process cannot acquire a controlling terminal.
  pid = fork();
  if (pid)
  {
    if (pid > 0)
    {
      exit(0);
    }
    else
    {
      syslog(LOG_ERR | LOG_USER, "Second fork failed: %m");
      return 1;
    }
  }

  return 0;
} // forkMainProcess

//==============================================================================
//
//
//
//==============================================================================
int redirectIoStreams(const char* logFile)
{
  // Close the standard streams.
  close(0);
  close(1);
  close(2);

  // Disable standard input.
  if (open("/dev/null", O_RDONLY) < 0)
  {
    syslog(LOG_ERR | LOG_USER, "Unable to open /dev/null: %m");
    return 1;
  }

  // Send standard output to a log file.
  const int    flags = O_WRONLY | O_CREAT | O_APPEND;
  const mode_t mode  = S_IRUSR  | S_IWUSR | S_IRGRP | S_IROTH;
  if (open(logFile, flags, mode) < 0)
  {
    syslog(LOG_ERR | LOG_USER, "Unable to open output file %s: %m", logFile);
    return 1;
  }

  // Send standard error to the same log file.
  if (dup(1) < 0)
  {
    syslog(LOG_ERR | LOG_USER, "Unable to dup output descriptor: %m");
    return 1;
  }

  return 0;
} // redirectIoStreams

} // namespace common
} // namespace tsc
